
/*******************************************************************************
 * Copyleft (c) 2021 Kcode
 *
 * @file    main.c
 * @brief   主程序，配合多个模块，实现在LCD显示屏上显示USB摄像头图像
 * @author  K
 * @version 0.0.1
 * @date    2021-07-26
 * @license MulanPSL-1.0
 *
 * 文件修改历史：
 * <时间>       | <版本>    | <作者>  | <描述>
 * 2021-07-28   | v0.0.1    | Kcode   | 主程序
 * -----------------------------------------------------------------------------
 ******************************************************************************/

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <signal.h>
#include <pthread.h>

#include "config.h"
#include "disp_manager.h"
#include "debug_manager.h"
#include "pic_operation.h"
#include "render.h"
#include "convert_manager.h"
#include "video_manager.h"

static pthread_t s_tPrintCameraFps;		//发送线程ID

static pthread_cond_t s_tPrintCameraFpsCon   = PTHREAD_COND_INITIALIZER;		//发送线程条件变量
static pthread_mutex_t s_tPrintCameraFpsMutex = PTHREAD_MUTEX_INITIALIZER;	//发送线程互斥量

typedef struct time_all
{
    struct timeval pretime;
    struct timeval gettime;
    struct timeval curtime;
}time_all;
    
static void *PrintCameraFps(void *pvoid)
{
    struct time_all fps_cal;

    int prems;
    int curms;
    float value, gettime, showtime;
    float fps;

    while (1) {
        /* 进入临界资源前，获得互斥量 */
        pthread_mutex_lock(&s_tPrintCameraFpsMutex);  

        /* pthread_cond_wait会先解除之前的pthread_mutex_lock锁定的s_tPrintCameraFpsMutex
         * 然后阻塞在等待队列里休眠，直到再次被唤醒
         * （大多数情况下是等待的条件成立而被唤醒，唤醒后，该进程会先锁定pthread_mutex_lock(&s_tNetDebugRecvMutex)
         */
        pthread_cond_wait(&s_tPrintCameraFpsCon, &s_tPrintCameraFpsMutex);

        /* 释放互斥量 */
        pthread_mutex_unlock(&s_tPrintCameraFpsMutex);

        /* 计算fps */
        fps_cal = (struct time_all)(*((struct time_all *)pvoid));

        prems = fps_cal.pretime.tv_sec * 1000 + fps_cal.pretime.tv_usec / 1000;
    	curms = fps_cal.curtime.tv_sec * 1000 + fps_cal.curtime.tv_usec / 1000;

        value = 1000000*(fps_cal.curtime.tv_sec-fps_cal.pretime.tv_sec)+fps_cal.curtime.tv_usec-fps_cal.pretime.tv_usec;    
        //showtime = (1000 / value);
        showtime = value/1000000;
        fps = 1 / showtime;

        value = 1000000*(fps_cal.gettime.tv_sec-fps_cal.pretime.tv_sec)+fps_cal.gettime.tv_usec-fps_cal.pretime.tv_usec;
        //gettime = (1000 / value);
        gettime = value / 1000000;
        //printf("prems tv_sec : %ld, tv_usec :%ld\n", fps_cal.pretime.tv_sec, fps_cal.pretime.tv_usec);
        //printf("curms tv_sec : %ld, tv_usec :%ld\n", fps_cal.curtime.tv_sec, fps_cal.curtime.tv_usec);
        printf("get video data from drv to app time  : %f s\n", gettime);
        printf("show video data from drv to app time  : %f s\n", showtime);
        printf("fps :    %f\n", fps); 

    }
    return NULL;
}

int main(int argc, char **argv)
{
	int error;
    int video_pixel_format;
    int display_pixel_format;
    int lcd_width;
    int lcd_height;
    int lcd_bpp;
    int topleft_x;
    int topleft_y;
    float k;
    T_VIDEODEVICE video_dev;
    T_VIDEOBUF video_buf;   
    T_VIDEOBUF convert_buf;
    T_VIDEOBUF zoom_buf;
    T_VIDEOBUF framebuf;
    PT_VIDEOBUF cur_video_buf;
    PT_VIDEOCONVERTOPR video_convert_opr; 
    struct time_all fps_cal_time;
    
    /*!
     * 初始化调试系统
     */
	error = DebugInit();
	if (error) {
		printf(APP_ERR"DebugInit error! File:%s Line:%d\n", __FILE__, __LINE__);
		return -1;
	}
	
	error = InitDebugChanel();
	if (error) {
		printf(APP_ERR"InitDebugChanel error! File:%s Line:%d\n", __FILE__, __LINE__);
		return -1;
	}

    /*!
     * 操作信息提示：video2lcd </dev/video0>
     */
    if (argc != 2)
    {
        DebugPrint(APP_NOTICE"Usage:\n");
        DebugPrint(APP_NOTICE"%s </dev/video0,1.....>\n", argv[0]);
        return -1;
    }
   
    /*!
     * 注册显示模块
     */
    error = DisplayInit();
    if (error)
    {
        DebugPrint(APP_ERR"DisplayInit err\n");
        return -1;
    }
    
    /* 选择显示设备 */
    SelectAndInitDefaultDispDev("fb");

    /* 获取显示屏参数 */
    GetDispResolution(&lcd_width, &lcd_height, &lcd_bpp);

    /* 获取显示屏显存 */
    GetVideoBufForDisplay(&framebuf);

    /* 设置显示器格式 */
    display_pixel_format = framebuf.pixel_format;
    
    /*!
     * 注册摄像头模块 
     */
    error = VideoInit();
    if (error)
    {
        DebugPrint(APP_ERR"VideoInit err\n");
        return -1;
    }    
    
    /*!
     * 初始化指定摄像头视频设备
     */
    error = VideoDeviceInit(argv[1], &video_dev);
    if (error < 0)
    {
        DebugPrint(APP_ERR"VideoDeviceInit for %s err\n", argv[1]);
        return -1;
    }
    
    /* 获取摄像头数据格式 */
    video_pixel_format = video_dev.video_opr->GetFormat(&video_dev);

    /*!
     * 注册转换模块
     */
    error = VideoConvertInit();
    if (error)
    {
        DebugPrint(APP_ERR"VideoConvertInit err\n");
        return -1;
    }
    
    /* 获取支持格式的转换处理结构体 */
    video_convert_opr = GetVIdeoConvertForFormats(video_pixel_format,
                                            display_pixel_format);
    if (video_convert_opr == NULL)
    {
        DebugPrint(APP_ERR"Can not support this format convert\n");
        return -1;
    }
    
    /*!
     * 启动摄像头设备 
     */
    error = video_dev.video_opr->StartDevice(&video_dev);
    if (error)
    {
        DebugPrint(APP_ERR"StartDevice for %s err\n", argv[1]);
        return -1;
    }

    memset(&zoom_buf, 0, sizeof(T_VIDEOBUF));
    memset(&video_buf, 0, sizeof(T_VIDEOBUF));
    memset(&convert_buf, 0, sizeof(T_VIDEOBUF));
    convert_buf.pixel_data.bpp = lcd_bpp;
    convert_buf.pixel_format = display_pixel_format;

    error = pthread_create(&s_tPrintCameraFps, NULL, PrintCameraFps, &fps_cal_time);
    /*!
     * 处理摄像头数据
     */
    while(1)
    {
        /*!
         * 读出摄像头数据 
         */
        gettimeofday(&fps_cal_time.pretime, NULL);
        error = video_dev.video_opr->GetFrame(&video_dev, &video_buf);
        if (error)
        {
            DebugPrint(APP_ERR"GetFrame for %s err\n", argv[1]);
            return -1;
        }

        gettimeofday(&fps_cal_time.gettime, NULL);
        
        cur_video_buf = &video_buf;
        printf("video_buf.pixel_data.TotalBytes: %d\n", video_buf.pixel_data.TotalBytes); 
        printf("video_buf.pixel_data.bpp: %d\n", video_buf.pixel_data.bpp);
        /*!
         * 转换为RGB 
         */
        if (video_pixel_format != display_pixel_format)
        {
            error = video_convert_opr->Convert(&video_buf, &convert_buf);
            if (error)
            {
                DebugPrint(APP_ERR"Convert for %s err\n", argv[1]);
                return -1;
            }

            cur_video_buf = &convert_buf;
            printf("convert_buf.pixel_data.TotalBytes: %d\n", convert_buf.pixel_data.TotalBytes);        
        }
        
        /*!
         * 若图像分辨率大于LCD，缩放 
         */
        if (cur_video_buf->pixel_data.height > lcd_height || \
                    cur_video_buf->pixel_data.width > lcd_width)
        {
            /* 设置缩放参数 */
            k = (float)cur_video_buf->pixel_data.height / (float)cur_video_buf->pixel_data.width;
            zoom_buf.pixel_data.width  = lcd_width;
            zoom_buf.pixel_data.height = lcd_width * k;
            if (zoom_buf.pixel_data.height > lcd_height)
            {
                zoom_buf.pixel_data.width  = lcd_height * k;
                zoom_buf.pixel_data.height = lcd_height; 
            }
            
            zoom_buf.pixel_data.bpp        = lcd_bpp;
            zoom_buf.pixel_data.linebytes  = zoom_buf.pixel_data.width * zoom_buf.pixel_data.bpp / 8;
            zoom_buf.pixel_data.TotalBytes = zoom_buf.pixel_data.linebytes * zoom_buf.pixel_data.height;

            if (zoom_buf.pixel_data.PixelDatas == NULL)
            {
                zoom_buf.pixel_data.PixelDatas = (unsigned char *)malloc(zoom_buf.pixel_data.TotalBytes);
            }

            /* 进行缩放 */
            PicZoom(&cur_video_buf->pixel_data, &zoom_buf.pixel_data);

            cur_video_buf = &zoom_buf;
        }

        printf("zoom_buf.pixel_data.TotalBytes: %d\n", zoom_buf.pixel_data.TotalBytes);
                    
        /*!
         * 合并到framebuffer 
         */
        /* 居中显示，计算此时的左上角坐标 */
        topleft_x = (lcd_width - cur_video_buf->pixel_data.width) / 2;
        topleft_y = (lcd_height - cur_video_buf->pixel_data.height) / 2;
        
        PicMerge(topleft_x, topleft_y, &cur_video_buf->pixel_data, &framebuf.pixel_data);

        /* 进入临界资源前，获得互斥量 */
        pthread_mutex_lock(&s_tPrintCameraFpsMutex);  
 
        /*!
         * 把framebuffer的数据刷到LCD，显示 
         */
        FlushPixelDatasToDev(&framebuf.pixel_data);
        printf("framebuf.pixel_data.TotalBytes: %d\n", framebuf.pixel_data.TotalBytes);
        gettimeofday(&fps_cal_time.curtime, NULL);        

        /* 客户端连接后，数据通过网络发送给客户端，采用线程的方式 */
        /* 唤醒netprint的发送线程 */
        pthread_cond_signal(&s_tPrintCameraFpsCon);

        /* 释放互斥量 */
        pthread_mutex_unlock(&s_tPrintCameraFpsMutex);
        /*!
         * 把数据放回队列 
         */
        error = video_dev.video_opr->PutFrame(&video_dev, &video_buf);
        if (error)
        {
            DebugPrint(APP_ERR"PutFrame for %s err\n", argv[1]);
            return -1;
        }        
    }

    /*!
     * 进行清理工作
     */
    //VideoConvertExit(video_convert_opr->name, &convert_buf);
    //VideoExit("v4l2", &video_buf);
}

